﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.IO;
using System.Runtime.Serialization;
using System.Data;

namespace QueueUnlimited.API
{
    public class DataStore : IDisposable, IConfiguration
    {
        private SqlConnection connection;

        public DataStore(SqlConnection connection)
        {
            if (connection == null)
                throw new ArgumentNullException("connection");
            this.connection = connection;
        }
        public DataStore(string sqlConnectionString)
        {
            if (string.IsNullOrEmpty(sqlConnectionString))
                throw new ArgumentNullException("sqlConnectionString");
            this.connection = new SqlConnection(sqlConnectionString);
        }

        public IConfiguration Configuration { get { return this; } }

        public void CleanUp()
        {
            using (var cc = new SqlCommand("Cleanup", connection))
            {
                cc.CommandType = System.Data.CommandType.StoredProcedure;
                if (connection.State != System.Data.ConnectionState.Open)
                    connection.Open();
                cc.ExecuteNonQuery();
            }
        }

        public void Enqueue(string eventName, string rawContent)
        {
            Enqueue(new GenericEvent(eventName, rawContent));
        }

        internal void Enqueue(GenericEvent eventMessage)
        {
            using (var cc = new SqlCommand("PublishEvent", connection))
            {
                cc.CommandType = System.Data.CommandType.StoredProcedure;
                cc.Parameters.AddWithValue("@Message", eventMessage.ToString());
                if (connection.State != System.Data.ConnectionState.Open)
                    connection.Open();
                cc.ExecuteNonQuery();
            }
        }

        public void CollectGarbage(int tries)
        {
            using (var c = new SqlCommand("CollectGarbage", connection))
            {
                c.CommandType = System.Data.CommandType.StoredProcedure;
                c.Parameters.AddWithValue("@Tries", tries);
                c.CommandTimeout = 500;
                if (connection.State != System.Data.ConnectionState.Open)
                    connection.Open();
                c.ExecuteNonQuery();
            }
        }

        void IConfiguration.Apply(Subscriber subscriber)
        {
            using (var cc = new SqlCommand("ApplySubscription", connection))
            {
                cc.CommandType = System.Data.CommandType.StoredProcedure;
                cc.Parameters.AddWithValue("@Events", GetEventTable(subscriber));
                cc.Parameters.AddWithValue("@ServiceName", subscriber.Name);
                if (connection.State != System.Data.ConnectionState.Open)
                    connection.Open();
                cc.ExecuteNonQuery();
            }
        }

        private DataTable GetEventTable(Subscriber subscriber)
        {
            var dataTable = new DataTable("MyTableValuedParameter");
            dataTable.Columns.Add(new DataColumn("ServiceName", typeof(string)));
            dataTable.Columns.Add(new DataColumn("EventName", typeof(string)));
            foreach (var eventName in subscriber.EventNames())
            {
                dataTable.Rows.Add(subscriber.Name, eventName);
            }
            return dataTable;
        }

        public int Count(Subscriber subscriber)
        {
            // TODO: refactor to SP:
            using (var cc = new SqlCommand(string.Format("SELECT COUNT(0) FROM [{0}]", "SQ_" + subscriber.Name), connection))
            {
                cc.CommandType = System.Data.CommandType.Text;
                if (connection.State != System.Data.ConnectionState.Open)
                    connection.Open();
                var numberOf = (int)cc.ExecuteScalar();
                return numberOf;
            }
        }

        /// <summary>
        /// Do not use this method for polling the subscribers!!!! This is a not performant method
        /// for receiving the next content of a subscriber.
        /// </summary>
        /// <returns></returns>
        public string DequeueRawContentOrNull(Subscriber subscriber)
        {
            string result = null;
            //TODO: refactor!
            using (var cc = new SqlCommand("DequeueRawOrNull", connection))
            {
                cc.CommandType = System.Data.CommandType.StoredProcedure;
                cc.Parameters.AddWithValue("@ServiceName", "SQ_" + subscriber.Name); // Refactor!
                if (connection.State != System.Data.ConnectionState.Open)
                    connection.Open();
                var reader = cc.ExecuteReader(CommandBehavior.CloseConnection | CommandBehavior.SingleRow | CommandBehavior.SingleResult);
                while (reader.Read())
                {
                    result = reader.GetString(reader.GetOrdinal("messageBodyAsString"));
                    reader.Close();
                    break;
                }
                return result;
            }
        }

        public void Dispose()
        {
            connection.Dispose();
        }

        public object CountOfUnprocessedMessages()
        {
            //TODO: Refactor to SP:
            using (var cc = new SqlCommand("SELECT COUNT(0) FROM [RootQueue]", connection))
            {
                cc.CommandType = System.Data.CommandType.Text;
                if (connection.State != System.Data.ConnectionState.Open)
                    connection.Open();
                var numberOf = (int)cc.ExecuteScalar();
                return numberOf;
            }
        }
    }
}


